﻿using HokjesSluiten;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace HokjesSluitenServer
{
    class Server
    {
        protected StreamWriter logfile;

        protected Map map;
        protected int mapWidth = 2;
        protected int mapHeight = 2;

        private static bool running = true;
        protected List<Bot>.Enumerator contestantEnumerator;

        public List<Bot> getPlayers()
        {
            string path = "D:/dotNet/HokjesSluiten/HokjesSluitenBot/bin/Debug";
            //  voeg bots toe aan list
            List<Bot> botList = new List<Bot>();
            
            //  Tijdelijke oplossing
            Bot b1 = new Bot("Piet");
            b1.start(String.Format("{0}/HokjesSluitenBot.exe", path));
            botList.Add(b1);

            Bot b2 = new Bot("Henkie");
            b2.start(String.Format("{0}/HokjesSluitenBot.exe", path));
            botList.Add(b2);
            //  Tijdelijke oplossing

            return botList;
        }

        public List<Bot> getNextContestants()
        {
            List<Bot> botList = getPlayers();

            foreach (Bot b1 in botList)
            {
                foreach (Bot b2 in botList)
                {
                    //  TODO: check if this match has been held
                    if (b2.botName != b1.botName)
                    {
                        List<Bot> fightingBots = new List<Bot>{ b1, b2 };

                        return fightingBots;
                    }
                }
            }

            return null;
        }

        public void startMatch(List<Bot> contestants)
        {
            string startBericht = Const.START + " " + String.Join(";", mapWidth, mapHeight);
            foreach (Bot bot in contestants)
            {
                WriteLog(String.Format("BOT {0}", bot.botName));
                bot.talkTo(startBericht);
            }
            WriteLog(startBericht);

            //  Init map
            map = new Map(mapWidth, mapHeight);
        }

        public Server(Boolean start)
        {
            // logfile
            string path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
            logfile = new StreamWriter(String.Format("{0}/{1}.txt", path.Replace("file:\\", String.Empty).Replace("\\", "/"), DateTime.Now.ToString("yyyyMMddHHmmssfff")), true);
            SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);

            try {
                //  Get Bots
                List<Bot> contestants = getNextContestants();

                // small graceperiod to boot up the programs
                Thread.Sleep(1000);
                startMatch(contestants);

                int moves = 0;
                Queue<string> moveMemory = new Queue<string>();
                contestantEnumerator = contestants.GetEnumerator();
                int turns = 0;
                while (running)
                {
                    //  Continue to loop through the contestants
                    if (moves == 0 && !contestantEnumerator.MoveNext())
                    {
                        contestantEnumerator = contestants.GetEnumerator();
                        contestantEnumerator.MoveNext();
                    }

                    Bot current = contestantEnumerator.Current;

                    if (moves == 0)
                    {
                        //  Show all moves the other bot made
                        for (int i = 0; i < moveMemory.Count; i++)
                            current.talkTo(moveMemory.Dequeue());
                        WriteLog(Const.TURN + " " + current.botName);
                    }
                    else
                    {
                        logfile.WriteLine("EXTRA-TURN " + current.botName);
                        moves--;
                    }

                    //  wacht max 4 sec op een antwoord
                    try
                    {
                        //  voer het antwoord in
                        //  check of het antwoord klopt
                        //  check of er een nieuw hokje is
                        //  check of er een winnaar is
                        //  geef wijzigingen door aan de volgende bot
                        
                        current.talkTo(Const.TURN);

                        string move = current.listenTo();
                        WriteLog(move);

                        moveMemory.Enqueue(move);

                        Streep streep = inputInterpreter(move);
                        streep.moveMadeBy = current;

                        if (streep == null)
                            throw new Exception();

                        List<Streep> streepResult = map.addStreep(streep, current);

                        if (streepResult == null)
                            throw new Exception();

                        //  hokje gemaakt, je mag nog een keer
                        if (streepResult.Count > 0)
                        {
                            moves++;
                            //logfile.WriteLine("HOK " + current.botName);
                        }

                    }
                    catch (Exception e)
                    {
                        //  Fout: volgende bot 2x
                        //  geef foute beurt door aan andere bot
                        //  geef de beurt nog 1x aan de andere bot
                        if (!contestantEnumerator.MoveNext())
                        {
                            contestantEnumerator = contestants.GetEnumerator();
                            contestantEnumerator.MoveNext();
                        }

                        moves++;
                    }

                    turns++;
                    if (isEndOfMatch())
                        running = false; 
                    
                }

                decideWinner(contestants);

            }
            catch (Exception e)
            {
                logfile.WriteLine(e.Message); 
            }

            finally {
                logfile.Close();
            }
        }

        public void decideWinner(List<Bot> contestants)
        {
            Bot winner = null;
            int hokjesByWinner = 0;

            foreach (Bot bot in contestants)
            {
                if (map.hokjesGewonnen(bot) > hokjesByWinner)
                    winner = bot;
            }

            foreach (Bot bot in contestants)
                bot.talkTo(Const.WIN + " " + winner.botName);
        }

        /**
         *  SET 0,0;0,1;
         */
        public Streep inputInterpreter(string inputText)
        {
            string[] arguments = inputText.Split(' ');
            
            switch (arguments[0])
            {
                case Const.POINT:
                    string[] coordArgs = arguments[1].Split(';');
                    string[] pointOneArgs = coordArgs[0].Split(',');
                    string[] pointTwoArgs = coordArgs[1].Split(',');

                    Point pointOne = new Point(int.Parse(pointOneArgs[0]), int.Parse(pointOneArgs[1]));
                    Point pointTwo = new Point(int.Parse(pointTwoArgs[0]), int.Parse(pointTwoArgs[1]));

                    Streep streep = new Streep(pointOne, pointTwo);

                    return streep;
            }

            return null;
        }

        private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
        {
            switch (ctrlType)
            {
                case CtrlTypes.CTRL_CLOSE_EVENT:
                    running = false;
                    break;
            }
            return true;
        }

        public void WriteLog(string line)
        {
            logfile.WriteLine(line);
            logfile.Flush();
        }

        public Boolean isEndOfMatch()
        {
            return map.movesLeft() == 0;
        }

        #region unmanaged
        // Declare the SetConsoleCtrlHandler function
        // as external and receiving a delegate.

        [DllImport("Kernel32")]
        public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);

        // A delegate type to be used as the handler routine
        // for SetConsoleCtrlHandler.
        public delegate bool HandlerRoutine(CtrlTypes CtrlType);

        // An enumerated type for the control messages
        // sent to the handler routine.

        public enum CtrlTypes
        {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT,
            CTRL_CLOSE_EVENT,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT
        }

        #endregion
    }
}
